#pragma once

#if !defined _FILE_SYNC_CORE_IMPLS_
#define _FILE_SYNC_CORE_IMPLS_

#include "File_Sync_Core.h"
#include "File_Sync_Core_i.h"
#include <ShlObj.h>



namespace hxc
{
	template<class t> class ThreadSafeContainer;
	class ThreadPool;

	template<class T>
	class ContainerPtr
	{
	private:
		template<class t> friend class ThreadSafeContainer;
		ContainerPtr& operator=(const ContainerPtr& value){}
	public:
		ContainerPtr(const ContainerPtr& value) : m_Container(value.m_Container){}
		explicit ContainerPtr(ThreadSafeContainer<T>* Container) : m_Container(Container){}

		~ContainerPtr()
		{
			::LeaveCriticalSection(&m_Container->Lock);
		}

		int AddCounter()
		{
			m_Container->ObjTotalCount++;
			return m_Container->ObjTotalCount;
		}

		__inline T* operator->()
		{
			return &m_Container->STLContainer;
		}
	private:
		ThreadSafeContainer<T>* m_Container;
	};

	template<class T>
	class ThreadSafeContainer
	{
	private:
		template<class t> friend class ContainerPtr;
		ThreadSafeContainer(const ThreadSafeContainer&){}
		ThreadSafeContainer& operator=(const ThreadSafeContainer&){}
	public:
		inline explicit ThreadSafeContainer(std::function<void(typename T::value_type&)> Destruct)
		{
			this->Destruct = Destruct;
			ObjTotalCount = 0;
			::InitializeCriticalSection(&Lock);
		}
		inline ThreadSafeContainer()
		{
			ObjTotalCount = 0;
			::InitializeCriticalSection(&Lock);
		}
		~ThreadSafeContainer()
		{
			::EnterCriticalSection(&this->Lock);
			if ((bool)Destruct)
			{
				_ASSERT(STLContainer.size() == ObjTotalCount);
				std::for_each (STLContainer.begin(), STLContainer.end(), Destruct);
			}
			::DeleteCriticalSection(&Lock);
		}
		inline ContainerPtr<T> GetContainer()
		{
			::EnterCriticalSection(&Lock);
			return ContainerPtr<T>(this);
		}
	private:
		int ObjTotalCount;
		T STLContainer;
		std::function<void(typename T::value_type&)> Destruct;
		CRITICAL_SECTION Lock;
	};

#define DECLARE_EVENT(varname,class)	\
	hxc::Event<void (class::*)(void) , class> varname;

	template<typename T, class BaseClass>
	class Event
	{
		Event(const Event& e){}
	public:
		Event():m_pInstance(nullptr)
		{
		}

		explicit Event(BaseClass* p)
		{
			m_pInstance = p;
		}

        template<typename FuncType>
        void operator =(FuncType Func)
        {
            ::InterlockedExchangePointer(&m_HandlerFunc, Func);
        }

        template<typename FuncType>
        void operator+=(FuncType value)
        {
            auto ptr = m_HandlerArray.GetContainer();
            ptr->push_back(static_cast<T>(value));
        }

        template<typename FuncType>
        void operator-=(FuncType value)
        {
            auto ptr = m_HandlerArray.GetContainer();
            for (auto Iterator = ptr->begin(); Iterator != ptr->end(); Iterator++)
            {
                if (*Iterator == static_cast<T>(value))
                {
                    ptr->erase(Iterator);
                    break;
                }
            }
        }

        bool operator()(void)
        {
            auto ptr = m_HandlerArray.GetContainer();
            for (auto Iterator = ptr->begin(); Iterator != ptr->end(); Iterator++)
            {
                T h = *Iterator;
                return (m_pInstance->*h)();
            }
            _ASSERT(FALSE);
            return false;
        }

		BaseClass* m_pInstance;
	protected:
		ThreadSafeContainer<std::vector<T>> m_HandlerArray;
	};

	struct _DataBuffer
	{
		DWORD dwDataLen;
		PVOID Buffer;
	};

	class File
	{
	public:
		File(const std::wstring& path, DWORD dwCreationDisposition);
		~File();
		DWORD ReadData(_DataBuffer& Buffer, class OverlapContext* lpol);
		DWORD WriteData(const _DataBuffer& Buffer, class OverlapContext* lpol);
		std::wstring MD5();
		std::wstring FileName();
		LARGE_INTEGER Length();
		std::wstring Path();
		DWORD FileName(const std::wstring& Name);
		DWORD Delete();
		void Refresh();
	private:
		std::wstring m_Path;
		std::wstring m_MD5;
		HANDLE m_hFile;
	};
	
	class Task
	{
	public:
		enum {
			TASK_DIRCHANGENOTIFY = 0x10,
			TASK_READFILE,
			TASK_WRITEFILE,
			TASK_FILENAMECHANGE,
			TASK_DELETEFILE,
			TASK_SCANDIR
			//FILEOPERATION_COMP_KEY = 50,
			//CHANGNOTIFY_COMP_KEY = 51
		};
		Task(DWORD Action, HJOB hJobItem);
		~Task();
	public:
		HJOB hJob;
		union {
			struct {
				HANDLE hDir;
				LPCWSTR lpDirPath;
				_DataBuffer ReadDirChangBuffer;
			}ReadDirChange;

			struct {
				File* m_pSourceFile;
				ThreadSafeContainer<std::vector<std::pair<LARGE_INTEGER, LARGE_INTEGER>>>* FilePointerSrc;
				File* m_pTargetFile;
			}CopyFile;

			struct {
				ThreadSafeContainer<std::list<std::wstring>>* ParamStack;
				std::wstring* RootPath;
			}ScanDir;

			std::pair<std::wstring, std::wstring>* FileNameChange;

			std::wstring* RemovedPathName;

		}Params;
		Event<bool(Task::*)(), Task> WorkProc;
	private:
		DWORD m_Action;
		bool DeleteFile();
		bool RenameFile();
		bool ScanDir();
	public:
		
	};

	class OverlapContext : public OVERLAPPED
	{
		friend class ThreadPool;
		OverlapContext(const OverlapContext&){}
		OverlapContext& operator=(const OverlapContext&){}
	public:
		OverlapContext() : Param(0), CallBack(this)
		{
			this->hEvent = NULL;
			this->Internal = 0;
			this->InternalHigh = 0;
			this->Offset = 0;
			this->OffsetHigh = 0;
		}
		DWORD_PTR Param;

		class Event1 : public Event<void(OverlapContext::*)(DWORD, DWORD), OverlapContext>
		{
		public:
            Event1(){}
            explicit Event1(OverlapContext* ptr) : Event(ptr){}
			void operator()(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred)
			{
				auto ptr = m_HandlerArray.GetContainer();
				for (auto Iterator = ptr->begin(); Iterator != ptr->end(); Iterator++)
				{
					void(OverlapContext::*h)(DWORD, DWORD) = *Iterator;
					(m_pInstance->*h)(dwErrorCode, dwNumberOfBytesTransferred);
				}
			}
		}CallBack;
	private:
		void ReadDirChangeHandler(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred);
	public:
		static void CALLBACK IoCallBack(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED lpol);
	};

	class ThreadPool
	{
	public:
		enum {BUFFERSIZE = 4096};
		ThreadPool();
		~ThreadPool();
		BOOL Initialize();
		BOOL Destroy();
		HJOB AddJobItem(const _JobItemInfo* pJobItemInfo);
		BOOL DelJobItem(HJOB hJob);
		BOOL StartMonitoring(HJOB hJob);
		BOOL ScanJobItem(HJOB hJob);
		void FreeBufferObjs(PVOID obj);
		PVOID FreeBufferObjs();
		void FreeOLObjs (OverlapContext* obj);
		OverlapContext* FreeOLObjs ();
        void ActiveQueueTimer();
	private:
		friend class Task;
		friend class OverlapContext;
		int m_MaxActiveItemCount;
		HANDLE m_hTimeQueue;
		HANDLE m_hTimer;
		ThreadSafeContainer<std::vector<_JobItem>> m_JobTable;
		//ThreadSafeContainer<std::list<Task*>> m_TaskTable;
		ThreadSafeContainer<std::vector<OverlapContext*>> m_OLTable;
		ThreadSafeContainer<std::list<Task*>> m_ActiveTasks;
		ThreadSafeContainer<std::vector<PVOID>> m_FreeBuffers;
		static void CALLBACK TimeQueueCallBack(PVOID pParam, BOOLEAN  bTimerOrWaitFired);
	};
};

typedef struct _JobItem
{
    inline _JobItem(const _JobItemInfo& value) : hDir(NULL)
    {
        ::InterlockedExchange(&lPendingOperations, 0);
        bCheckMD5 = value.bCheckMD5;
        bSafeCopy = value.bSafeCopy;

        ExcludePath = value.lpExcludePath;
        ExcludeType = value.lpExcludeType;
        SourceDir = value.lpSourceDir;
        TargetDir = value.lpTargetDir;

        ChangeList = new hxc::ThreadSafeContainer<std::map<std::wstring, hxc::Task*>>;
    }

    inline void IncreasePendingOperCount()
    {
        ::InterlockedExchangeAdd(&lPendingOperations, 1);
    }

    inline void DecreasePendingOperCount()
    {
        ::InterlockedExchangeAdd(&lPendingOperations, -1);
    }
    BOOL bCheckMD5;
    BOOL bSafeCopy;
    HANDLE hDir;
    volatile long lPendingOperations;
    std::wstring ExcludePath;
    std::wstring ExcludeType;
    std::wstring SourceDir;
    std::wstring TargetDir;
    hxc::ThreadSafeContainer<std::map<std::wstring, hxc::Task*>>* ChangeList;
private:
    _JobItem(){}
}JOBITEM, *PJOBITEM;


class ATL_NO_VTABLE CExtractIconImpl :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CExtractIconImpl, &CLSID_ExtractIconImpl>,
    public IPersistFile,
    public IExtractIcon
{
public:
    CExtractIconImpl()
    {
    }

    DECLARE_REGISTRY_RESOURCEID(IDR_ATL_EXTRACTICON)

    //DECLARE_GET_CONTROLLING_UNKNOWN()

    DECLARE_NOT_AGGREGATABLE(CExtractIconImpl)

    BEGIN_COM_MAP(CExtractIconImpl)
        COM_INTERFACE_ENTRY(IExtractIcon)
        COM_INTERFACE_ENTRY(IPersistFile)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        HRESULT hr = E_FAIL;
        return hr;
    }

    void FinalRelease()
    {
    }
public:
    // IPersistFile
    STDMETHOD(GetClassID)( CLSID* )       { return E_NOTIMPL; }
    STDMETHOD(IsDirty)()                  { return E_NOTIMPL; }
    STDMETHOD(Save)( LPCOLESTR, BOOL )    { return E_NOTIMPL; }
    STDMETHOD(SaveCompleted)( LPCOLESTR ) { return E_NOTIMPL; }
    STDMETHOD(GetCurFile)( LPOLESTR* )    { return E_NOTIMPL; }
    STDMETHOD(Load)( LPCOLESTR szFile, DWORD /*dwMode*/ )
    { 
        m_szFileName = szFile;
        return S_OK;
    }
    // IExtractIcon
    STDMETHODIMP GetIconLocation(UINT uFlags, LPTSTR szIconFile, UINT cchMax, int* piIndex, UINT* pwFlags);

    STDMETHODIMP Extract(LPCTSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize);
private:
    std::basic_string<OLECHAR> m_szFileName;
};
OBJECT_ENTRY_AUTO(__uuidof(ExtractIconImpl), CExtractIconImpl)

#endif